import NodeOAuthServer, { Request, Response, InvalidArgumentError } from 'oauth2-server'
import Promise from 'bluebird'

var handleResponse = function(req, res, response) {
  if (response.status === 302) {
    var location = response.headers.location;
    delete response.headers.location;
    
    res.writeHead(response.status, {
      'Location': location
    });
    res.end()
  } else {
    res.writeHead(response.status, response.headers || {});
    res.end(JSON.stringify(response.body));
  }
};

var handleError = function(e, req, res, response, next) {
  if (this.useErrorHandler === true) {
    next(e);
  } else {
    res.writeHead(200, e.name, response.headers || {});
    res.end(JSON.stringify(e));
  }
};

export default class ExpressOAuthServer {
  constructor(options) {
    options = options || {};

    if (!options.model) {
      throw new InvalidArgumentError('Missing parameter: `model`');
    }
  
    this.useErrorHandler = options.useErrorHandler ? true : false;
    delete options.useErrorHandler;
  
    this.continueMiddleware = options.continueMiddleware ? true : false;
    delete options.continueMiddleware;
  
    this.server = new NodeOAuthServer(options);
  }

  authenticate(options) {
    var that = this;
    return function(req, res, next) {
      var request = new Request(req);
      var response = new Response(res);
      return Promise.bind(that)
        .then(function() {
          return this.server.authenticate(request, response, options);
        })
        .tap(function(token) {
          res.locals.oauth = { token: token };
          next();
        })
        .catch(function(e) {
          return handleError.call(this, e, req, res, null, next);
        });
    };
  }

  authorize(options) {
    var that = this;

    return function(req, res, next) {
      var request = new Request(req);
      var response = new Response(res);

      return Promise.bind(that)
        .then(function() {
          return this.server.authorize(request, response, options);
        })
        .tap(function(code) {
          res.locals.oauth = { code: code };
          if (this.continueMiddleware) {
            next();
          }
        })
        .then(function() {
          return handleResponse.call(this, req, res, response);
        })
        .catch(function(e) {
          return handleError.call(this, e, req, res, response, next);
        });
    };
  }

  token(options) {
    var that = this;
    return function(req, res, next) {
      var request = new Request(req);
      var response = new Response(res);

      return Promise.bind(that)
        .then(function() {
          return this.server.token(request, response, options);
        })
        .tap(function(token) {
          res.locals.oauth = { token: token };
          if (this.continueMiddleware) {
            next();
          }
        })
        .then(function() {
          return handleResponse.call(this, req, res, response);
        })
        .catch(function(e) {
          return handleError.call(this, e, req, res, response, next);
        });
    };
  }
}
